/*==================================================================
  la_firmware.cpp :
------------------------------------------------------------------*/
 
/*==================================================================
  Includes :
------------------------------------------------------------------*/
#include "firmware.h"

// global vars
unsigned      int ConfigRegisters[NR_OF_ConfigRegisters];
unsigned      int ReceiveMsgCmd;
              int ReceiveMsgParamCount;
unsigned long int ReceiveMsgParam[MAX_PARAM_COUNT];   

// prototypes of local functions
int SerialReceiveTakt();      
void CheckMemorySize();

void ProcessCmdMemory();        
void ProcessCmdRegister();        
void ProcessCmdSamples();        
void ProcessCmdFill();        
void ProcessCmdPrint();

void SetRegister(int RegNr, unsigned int Value);

/*==================================================================
  main :
------------------------------------------------------------------*/
int main(void)
{  int i;

   SetIoDefaults();
   
   // disable watchdog
   PinClear(MCUSR, WDRF);
   WDTCSR=0x18;
   WDTCSR=0x00;

   sei(); // enable interrupts
   uart_init (UART_BAUD_SELECT_DOUBLE_SPEED (115200L, 20000000));
                                                 
   // hello messages                                              
   uart_puts("Logic Analyser ready.\r\n");
   
   ConfigRegisters[REG_SW_VERSION] = 1;   
   MsgOpen('H');
   MsgParam(ConfigRegisters[REG_SW_VERSION]);
   MsgClose();      
   
   CheckMemorySize();               // determine memory size & set register

   // register defaults (for test)
   ConfigRegisters[REG_COMPRESS]       = 0;     // disable compression
   ConfigRegisters[REG_SAMPLE_RATE]    = 10;    // sample at 10 * 100ns => 1 MHz
   ConfigRegisters[REG_SAMPLE_SIZE]    = 4;     // sample 4 pages
   ConfigRegisters[REG_PRESAMPLE_SIZE] = 2;     // pre-sample 2 pages

   for(;;) {

      if (SerialReceiveTakt()) {
         // process command        
//         uart_puts("Cmd received: ");
//         uart_putc(ReceiveMsgCmd);
//         uart_putc('\r');
//         uart_putc('\n');
//         uart_puts("# params received: ");
//         PutHex(ReceiveMsgParamCount);
//         uart_putc('\r');
//         uart_putc('\n');
         
         for (i=0; i<ReceiveMsgParamCount; i++) {
            uart_puts("Param received: ");
            PutHex(ReceiveMsgParam[i]);
            uart_putc('\r');
            uart_putc('\n');
         }
         switch(ReceiveMsgCmd) {
            case 'm' : {
               // Memory command 
               ProcessCmdMemory();
               break;
            }
            case 'p' : {
               // Print (register) command
               ProcessCmdPrint();
               break;
            }
            case 'r' : {
               // (set) Register command
               ProcessCmdRegister();
               break;
            }
            case 's' : {
               // Samples command    
               RedLed(1);
               GetSamples(ReceiveMsgParam[0], ReceiveMsgParam[1], ReceiveMsgParam[2]);
               RedLed(0);
               break;
            }
            case 'w' : {
               // write
               ProcessCmdFill();
               break;
            }
            default : {
               ConfigRegisters[REG_STATUS] = RS_UNKNOWN_COMMAND;
            }
         }
      }
   }
}

//---------------------------------------------------------------------------
// ProcessCmdPrint - print register values
//---------------------------------------------------------------------------        
// Params:
// 0 = first register
// 1 = nr of ConfigRegisters
//---------------------------------------------------------------------------
void ProcessCmdPrint()
{  unsigned int i;
   
   if(ReceiveMsgParamCount != 2) {
      // two params required
      ConfigRegisters[REG_STATUS] = RS_WRONG_NR_PARAM;
      return;
   }
   
   MsgOpen(ReceiveMsgCmd & 0xDF); // upper case
   
   for (i=0; i<ReceiveMsgParam[1]; i++) {
      MsgParam(ConfigRegisters[ReceiveMsgParam[0] + i]);
   }
   MsgClose();
}

//---------------------------------------------------------------------------
// ProcessCmdRegister - Set one or more ConfigRegisters
//---------------------------------------------------------------------------
// Params:
// 0 = first register
// 1 = first register data
// [2 = second register data
// [3 = third register data[(etc)]]] 
//---------------------------------------------------------------------------
void ProcessCmdRegister()
{  int i;
   
   if(ReceiveMsgParamCount < 2) {
      // at least two params required
      ConfigRegisters[REG_STATUS] = RS_MISSING_PARAM;
      return;
   }
   for (i=0; i<(ReceiveMsgParamCount-1); i++) {
      SetRegister(ReceiveMsgParam[0] + i, ReceiveMsgParam[1 + i]);
   }
}

//---------------------------------------------------------------------------
// SetRegister - internal use only
//---------------------------------------------------------------------------
// This function checks if a register can be changed and if so, it sets 
// the indicated register to Value.
//---------------------------------------------------------------------------
void SetRegister(int RegNr, unsigned int Value)
{
   if (RegNr >= NR_OF_ConfigRegisters) {
      
      ConfigRegisters[REG_STATUS] = RS_INVALID_PARAM;
      return;
   }
   
   switch(RegNr) {
      case REG_SW_VERSION  : break;
      case REG_STATUS      : break;
      case REG_SW_MEMSIZE  : break;
      default : {
         // these ConfigRegisters can be writen 
         ConfigRegisters[RegNr] = Value;
         break;
      }   
   }
}

//---------------------------------------------------------------------------
// ProcessCmdMemory - Prints the memory requested
//---------------------------------------------------------------------------
// Print sample memory content
// - todo: compressing support            
// Params:
// 0 - first memory address
// 1 - nr of bytes requested
//---------------------------------------------------------------------------
void ProcessCmdMemory()
{  unsigned long int i;
   
   if(ReceiveMsgParamCount != 2) {
      // two params required
      ConfigRegisters[REG_STATUS] = RS_WRONG_NR_PARAM;
      return;
   } 
   
   // Go to memory location
   SetCounterPosition(ReceiveMsgParam[0]);

   // send requested bytes in frame
   MsgOpen(ReceiveMsgCmd & 0xDF); // upper case of command       
   
   if (ConfigRegisters[REG_COMPRESS]) MsgCompress();
   
   for (i=0; i<ReceiveMsgParam[1]; i++) {
      MsgParam(MemoryReadPulse());
   }                    
   MsgClose();
}

//---------------------------------------------------------------------------
// ProcessCmdFill - Fill a block of memory 
//---------------------------------------------------------------------------
// This command is intended for diagnostics. It fills a designated block of
// the sample memory with a specific value. Use CmdMemory to verify the 
// content of the sample memory.
//
// Params:
// 0 - start address
// 1 - number of bytes to fill
// 2 - fill value
// 
//---------------------------------------------------------------------------
void ProcessCmdFill()
{  int unsigned long i;
           
   uart_puts("Fill");

   if(ReceiveMsgParamCount != 3) {
      // three params required
      ConfigRegisters[REG_STATUS] = RS_WRONG_NR_PARAM;
      return;
   } 
   
   // Set address counter
   SetCounterPosition(ReceiveMsgParam[0]);
                         
   // Write data
   for (i=0; i<ReceiveMsgParam[1]; i++) {
      MemoryWritePulse(ReceiveMsgParam[2]);  
      uart_putc('.');
   }            

   // done
   uart_puts("\r\n");
}


void CheckMemorySize()
{  int i;
   
   // set 0xEA at location 0.
   SetCounterPosition(0);
   MemoryWritePulse(0xEA);
                             
   // Verify location 0                             
   SetCounterPosition(0);
   i = MemoryReadPulse();   
   if (i != 0xEA) {
      uart_puts("Memory error\r\n");      
      ConfigRegisters[REG_SW_MEMSIZE] = 0;
      return;
   }                   

   // set 0x55 at location 0x8000.
   SetCounterPosition(0x8000);
   MemoryWritePulse(0x55);

   // read location 0
   SetCounterPosition(0);
   i = MemoryReadPulse();   
   if (i == 0x55) {               
      // 0x800 maps to, so we have 32k bytes of memory
      //uart_puts("32kbyte of Memory\r\n");      
      ConfigRegisters[REG_SW_MEMSIZE] = 32 * 4;   // * 256 byte of memory
      return;
   }                   

   // set 0x55 at location 0x10000.
   SetCounterPosition(0x10000);
   MemoryWritePulse(0x56);

   // read location 0
   SetCounterPosition(0);
   i = MemoryReadPulse();   
   if (i == 0x56) {               
      // 0x1000 maps to, so we have 64k bytes of memory
      //uart_puts("64kbyte of Memory\r\n");      
      ConfigRegisters[REG_SW_MEMSIZE] = 64 * 4;   // * 256 byte of memory
      return;
   }                   

   // set 0x55 at location 0x20000.
   SetCounterPosition(0x20000);
   MemoryWritePulse(0x57);

   // read location 0
   SetCounterPosition(0);
   i = MemoryReadPulse();   
   if (i == 0x57) {               
      // 0x2000 maps to, so we have 128k bytes of memory
      //uart_puts("128kbyte of Memory\r\n");   
      ConfigRegisters[REG_SW_MEMSIZE] = 128 * 4;   // * 256 byte of memory
      return;   
   }                   

   uart_puts("!!! OVER 128kbyte of Memory !!!\r\n");      
   ConfigRegisters[REG_SW_MEMSIZE] = 0xFFFF;   // * 256 byte of memory
}
